/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package dao;

import dao.exceptions.IllegalOrphanException;
import dao.exceptions.NonexistentEntityException;
import entities.User;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import entities.Contest;
import java.util.ArrayList;
import java.util.List;
import entities.Submission;
import exceptions.*;
import services.ContestService;
import util.EnumSearchParameter;
import vo.VOUser;

/**
 *
 * @author sbaldrich
 */
public class UserJpaController {

    public UserJpaController()
    {
        emf = Persistence.createEntityManagerFactory("CodeMastersAlphaPU");
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager()
    {
        return emf.createEntityManager();
    }

    public void create(User user) throws Exception
    {
        if (user.getContestList() == null)
        {
            user.setContestList(new ArrayList<Contest>());
        }
        if (user.getSubmissionList() == null)
        {
            user.setSubmissionList(new ArrayList<Submission>());
        }
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            em.getTransaction().begin();
            List<Contest> attachedContestList = new ArrayList<Contest>();
            for (Contest contestListContestToAttach : user.getContestList())
            {
                contestListContestToAttach = em.getReference(contestListContestToAttach.getClass(), contestListContestToAttach.getIdContest());
                attachedContestList.add(contestListContestToAttach);
            }
            user.setContestList(attachedContestList);
            List<Submission> attachedSubmissionList = new ArrayList<Submission>();
            for (Submission submissionListSubmissionToAttach : user.getSubmissionList())
            {
                submissionListSubmissionToAttach = em.getReference(submissionListSubmissionToAttach.getClass(), submissionListSubmissionToAttach.getIdSubmission());
                attachedSubmissionList.add(submissionListSubmissionToAttach);
            }
            user.setSubmissionList(attachedSubmissionList);
            em.persist(user);
            for (Contest contestListContest : user.getContestList())
            {
                contestListContest.getUserList().add(user);
                contestListContest = em.merge(contestListContest);
            }
            for (Submission submissionListSubmission : user.getSubmissionList())
            {
                User oldUserOfSubmissionListSubmission = submissionListSubmission.getUser();
                submissionListSubmission.setUser(user);
                submissionListSubmission = em.merge(submissionListSubmission);
                if (oldUserOfSubmissionListSubmission != null)
                {
                    oldUserOfSubmissionListSubmission.getSubmissionList().remove(submissionListSubmission);
                    oldUserOfSubmissionListSubmission = em.merge(oldUserOfSubmissionListSubmission);
                }
            }
            em.getTransaction().commit();
        }
        catch (Exception e)
        {
            System.out.println("Exception at UserJpaController: " + e.getMessage());
            throw e;
        }
        finally
        {
            if (em != null)
            {
                em.close();
            }
        }
    }

    public void edit(User user) throws IllegalOrphanException, NonexistentEntityException, Exception
    {
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            em.getTransaction().begin();
            User persistentUser = em.find(User.class, user.getIdUser());
            List<Contest> contestListOld = persistentUser.getContestList();
            List<Contest> contestListNew = user.getContestList();
            List<Submission> submissionListOld = persistentUser.getSubmissionList();
            List<Submission> submissionListNew = user.getSubmissionList();
            List<String> illegalOrphanMessages = null;
            for (Submission submissionListOldSubmission : submissionListOld)
            {
                if (!submissionListNew.contains(submissionListOldSubmission))
                {
                    if (illegalOrphanMessages == null)
                    {
                        illegalOrphanMessages = new ArrayList<String>();
                    }
                    illegalOrphanMessages.add("You must retain Submission " + submissionListOldSubmission + " since its user field is not nullable.");
                }
            }
            if (illegalOrphanMessages != null)
            {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            List<Contest> attachedContestListNew = new ArrayList<Contest>();
            for (Contest contestListNewContestToAttach : contestListNew)
            {
                contestListNewContestToAttach = em.getReference(contestListNewContestToAttach.getClass(), contestListNewContestToAttach.getIdContest());
                attachedContestListNew.add(contestListNewContestToAttach);
            }
            contestListNew = attachedContestListNew;
            user.setContestList(contestListNew);
            List<Submission> attachedSubmissionListNew = new ArrayList<Submission>();
            for (Submission submissionListNewSubmissionToAttach : submissionListNew)
            {
                submissionListNewSubmissionToAttach = em.getReference(submissionListNewSubmissionToAttach.getClass(), submissionListNewSubmissionToAttach.getIdSubmission());
                attachedSubmissionListNew.add(submissionListNewSubmissionToAttach);
            }
            submissionListNew = attachedSubmissionListNew;
            user.setSubmissionList(submissionListNew);
            user = em.merge(user);
            for (Contest contestListOldContest : contestListOld)
            {
                if (!contestListNew.contains(contestListOldContest))
                {
                    contestListOldContest.getUserList().remove(user);
                    contestListOldContest = em.merge(contestListOldContest);
                }
            }
            for (Contest contestListNewContest : contestListNew)
            {
                if (!contestListOld.contains(contestListNewContest))
                {
                    contestListNewContest.getUserList().add(user);
                    contestListNewContest = em.merge(contestListNewContest);
                }
            }
            for (Submission submissionListNewSubmission : submissionListNew)
            {
                if (!submissionListOld.contains(submissionListNewSubmission))
                {
                    User oldUserOfSubmissionListNewSubmission = submissionListNewSubmission.getUser();
                    submissionListNewSubmission.setUser(user);
                    submissionListNewSubmission = em.merge(submissionListNewSubmission);
                    if (oldUserOfSubmissionListNewSubmission != null && !oldUserOfSubmissionListNewSubmission.equals(user))
                    {
                        oldUserOfSubmissionListNewSubmission.getSubmissionList().remove(submissionListNewSubmission);
                        oldUserOfSubmissionListNewSubmission = em.merge(oldUserOfSubmissionListNewSubmission);
                    }
                }
            }
            em.getTransaction().commit();
        } catch (Exception ex)
        {
            String msg = ex.getLocalizedMessage();
            System.out.println("Exception at Jpa Controller: " + ex.getMessage());
            if (msg == null || msg.length() == 0)
            {
                Integer id = user.getIdUser();
                if (findUser(id) == null)
                {
                    throw new NonexistentEntityException("The user with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally
        {
            if (em != null)
            {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws IllegalOrphanException, NonexistentEntityException
    {
        EntityManager em = null;
        try
        {
            em = getEntityManager();
            em.getTransaction().begin();
            User user;
            try
            {
                user = em.getReference(User.class, id);
                user.getIdUser();
            } catch (EntityNotFoundException enfe)
            {
                throw new NonexistentEntityException("The user with id " + id + " no longer exists.", enfe);
            }
            List<String> illegalOrphanMessages = null;
            List<Submission> submissionListOrphanCheck = user.getSubmissionList();
            for (Submission submissionListOrphanCheckSubmission : submissionListOrphanCheck)
            {
                if (illegalOrphanMessages == null)
                {
                    illegalOrphanMessages = new ArrayList<String>();
                }
                illegalOrphanMessages.add("This User (" + user + ") cannot be destroyed since the Submission " + submissionListOrphanCheckSubmission + " in its submissionList field has a non-nullable user field.");
            }
            if (illegalOrphanMessages != null)
            {
                throw new IllegalOrphanException(illegalOrphanMessages);
            }
            List<Contest> contestList = user.getContestList();
            for (Contest contestListContest : contestList)
            {
                contestListContest.getUserList().remove(user);
                contestListContest = em.merge(contestListContest);
            }
            em.remove(user);
            em.getTransaction().commit();
        } finally
        {
            if (em != null)
            {
                em.close();
            }
        }
    }

    public List<User> findUserEntities()
    {
        return findUserEntities(true, -1, -1);
    }

    public List<User> findUserEntities(int maxResults, int firstResult)
    {
        return findUserEntities(false, maxResults, firstResult);
    }

    private List<User> findUserEntities(boolean all, int maxResults, int firstResult)
    {
        EntityManager em = getEntityManager();
        try
        {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(User.class));
            Query q = em.createQuery(cq);
            if (!all)
            {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally
        {
            em.close();
        }
    }

    public User findUser(Integer id)
    {
        EntityManager em = getEntityManager();
        try
        {
            return em.find(User.class, id);
        } finally
        {
            em.close();
        }
    }

    public User findUser(String handle)throws Exception
    {
        EntityManager em = getEntityManager();
        try
        {
            Query q = em.createNamedQuery("User.findByHandle");
            q.setParameter("handle",handle);
            return (User)q.getSingleResult();
        }
        catch(Exception e)
        {
            if(e instanceof EntityNotFoundException)
                throw new NonExistentObjectException("User");
            throw new DatabaseException("UserJPAController.findUser");
         }
        finally
        {
            em.close();
        }
    }

    public int getUserCount()
    {
        EntityManager em = getEntityManager();
        try
        {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<User> rt = cq.from(User.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally
        {
            em.close();
        }
    }

    public List<User> findAll(EnumSearchParameter param ,String value) throws Exception
    {
        EntityManager em = getEntityManager();
        try
        {
            Query q = em.createNamedQuery("User.findBy"+param.value());
            q.setParameter(param.value(), value);
            return (List<User>)q.getResultList();
        } catch (Exception e)
        {
            throw new DatabaseException();
}
    }

    public List<User> findAll() throws Exception
    {
        EntityManager em = getEntityManager();
        try
        {
            Query q = em.createNamedQuery("User.findAll");
            return (List<User>)q.getResultList();
        } catch (Exception e)
        {
            throw new DatabaseException();
        }
    }

}